<script setup>
import { ref } from 'vue'

const wrapCanvas = ref(null)
const foreignObjectCanvas = ref(null)

const showText = ref('')

const getLineHeight = (element) => {
    const computedStyle = window.getComputedStyle(element)
    let lineHeight = computedStyle.lineHeight

    // 处理未设置获取"normal"的情况
    if (lineHeight === 'normal') {
        // 通过临时元素测量实际行高
        const temp = document.createElement('div')
        temp.style.font = computedStyle.font
        temp.style.padding = '0'
        temp.style.margin = '0'
        temp.style.border = '0'
        temp.style.position = 'absolute'
        temp.style.visibility = 'hidden'
        temp.innerHTML = 'A'

        document.body.appendChild(temp)
        const height1 = temp.clientHeight

        temp.innerHTML = 'A<br>A'
        const height2 = temp.clientHeight
        const lineHeight = height2 - height1

        document.body.removeChild(temp)
        return lineHeight
    }

    return parseFloat(lineHeight)
}

const autoWrapText = (context, text, x, y, maxWidth, lineHeight) => {
    if (!context) {
        return
    }

    let canvas = context.canvas

    if (typeof maxWidth == 'undefined') {
        maxWidth = (canvas && canvas.width) || 300
        maxWidth -= x
    }

    if (typeof lineHeight == 'undefined') {
        lineHeight = getLineHeight(canvas)
    }
    
    let textAry = text.split('')
    let lineText = ''

    for (let i = 0; i < textAry.length; i++) {
        let metrics = context.measureText(lineText + textAry[i])

        if (metrics.width >= maxWidth) {
            context.fillText(lineText, x, y)
            y += lineHeight
            lineText = textAry[i]
        } else {
            lineText += textAry[i]
        }
    }

    if (lineText) {
        context.fillText(lineText, x, y)
    }
}

const foreignObjectWrapText = (context, text) => {
    let canvas = context.canvas
    let width = canvas.width
    let height = canvas.height
    
    let tempImg = new Image()
    tempImg.width = width
    tempImg.height = height

    tempImg.onload = function () {
        context.drawImage(this, 0, 0, width, height)
    }

    tempImg.src = 'data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="'+ width +'" height="'+ height +'"><body xmlns="http://www.w3.org/1999/xhtml" style="margin:0;font:'+ context.font +';">'+ text +'</body></foreignObject></svg>';
}

const onTransTextClick = () => {
    let context = wrapCanvas.value.getContext('2d')
    context.font = '14px 微软雅黑'
    context.textBaseline = 'top'
    autoWrapText(context, showText.value, 10, 10, 300)

    let foreignContext = foreignObjectCanvas.value.getContext('2d')
    foreignObjectWrapText(foreignContext, showText.value, 10, 10, 300, 300)
}
</script>

<template>
    <div class="wrapper">
        <div style="margin-bottom: 10px;">
            <el-input
                v-model="showText"
                style="width: 500px"
                :rows="4"
                type="textarea"
            />
            <el-button type="primary" size="small" @click="onTransTextClick" style="margin-left: 5px;">转换</el-button>
        </div>

        <div>通过measureText()逐字累加计算文字宽度来确定换行位置</div>
        <canvas ref="wrapCanvas" width="500" height="300"></canvas>
        
        <div>{{ `通过SVG <foreignObject>让HTML转换成canvas图片的原理` }}</div>
        <canvas ref="foreignObjectCanvas" width="500" height="300"></canvas>
    </div>
</template>

<style scoped>
.wrapper {
    position: relative;
    height: 100%;
    padding: 8px;
    overflow: auto;
}

canvas {
    display: block;
    border: 1px solid #ccc;
}

:deep(input) {
    resize: none;
}
</style>